<?php
declare (strict_types = 1);

namespace app\controller\adminapi\v1;



use app\BaseController;
use app\Request;
//use support\Request;
//use support\Response;
//use Webman\Http\Request;
//use Webman\Http\Response;


use think\facade\Db;
use think\facade\Cache;
use think\exception\ValidateException;
use think\helper\Str;
use think\Validate;


use Emarref\Jwt\Claim;
use Emarref\Jwt\Encoding\Base64;
use Emarref\Jwt\Token;
use Emarref\Jwt\Jwt;
use Emarref\Jwt\Algorithm;
use Emarref\Jwt\Encryption\Factory;
use Emarref\Jwt\Verification\Context;


use support\Container;


use Exception;
use Throwable;



class LoginController extends BaseController
{

    /**
     * 构造方法
     * LoginController constructor.
     */
	public function __construct()
	{
        //$this->services = new UserServices;
        //$this->services = Container::get(UserServices::class);
	}

    
    //登录，成功生成Token并返回
    public function login(Request $request)
    {
    	/*$account = $request->post("account");
    	$password = $request->post("password");
    	$captcha = $request->post("captcha");
    	$cid = $request->post("cid");	//验证码的id（直接作为jwt的jwtID字段）
		*/

        $data = $request->getParams([
            ['account', '', 'trim'],
            ['password', ''],
            ['captcha'],
            ['cid'],
        ]);

		
		static::validate( $data, [
			'account' => 'require|alphaNum|length:4,32',
			'password' => 'require|length:6,32',
			'captcha' => 'require|alphaNum',
			'cid' => 'require',
		]);


		/*try {
			static::validate( [
				'account' => $account,
				'password' => $password,
			], [
				'account' => 'require',
				'password' => 'require',
			]);
		}
		catch (ValidateException $e) {
			// 验证失败 输出错误信息
			var_dump($e->getError());
		}*/


    	if(!Container::get(\app\services\CaptchaServices::class)->verifyCaptcha($data['captcha'], $data['cid']))
    	{
            return ret_value(-1, "验证码错误");
        	//return json([ "code"=> -1, "msg" => "验证码错误" ]);
    	}
		
		$lms_common = config('lms_common');
		$password = md5($lms_common["password_salt"] . $data['password']);

        $user = \think\facade\Db::
			//connect('local')->
			//->table('admin_user')->
			name('admin_user')->
			//field(true)->	//和 field('*') 的区别是，前者会列出所有列名
			field('user.*, role.role_name, role.rules, role.status AS role_status')->
			alias("user")->
			leftJoin('admin_role role','user.roles = role.id')->
			//where('id', 1)->
			where('account', $data['account'])->
			where('pwd', $password)->
			where('user.delete_time', null)->
			where('role.delete_time', null)->
			where('role.status', 1)->
			//column('*')->
			find()
		;

		//var_dump( $user["id"], $user["login_name"],  $user["nick_name"], $user["sex"], $user["password"]);
		//var_dump( $user);

		if($user === null)
			return ret_value(-2, "用户名或密码错误");
			//return json([ "code"=> -2, "msg" => "用户名或密码错误" ]);

		if($user['status'] !== 1)
			return ret_value(-3, "账户状态异常");
			//return json([ "code"=> -3, "msg" => "账户状态异常" ]);

		$usertoken = ["id" => $user["id"], "roles" => $user["roles"]];
        $userinfo = ["account" => $user["account"], 
        	"id" => $user["id"], 
        	//"mobile" => $user["mobile"], 
        	"real_name" => $user["real_name"],
        	"roles" => $user["roles"], 
        	"level" => $user["level"], 
        	"role_name" => $user["role_name"], 
        	//"avatar" => $user["avatar"], 
        ];


        $rules = \think\facade\Db::
			//connect('local')->
			name('admin_permission')->
			//whereIn('id', $user['rules'])->
			//whereOr([[['id', 'IN', $user['rules']]], [['level', '=', 0]]])->
			// 满足条件后执行
			when($user["level"] > 0, function ($query) use ($user) {
					$query->whereIn('id', $user['rules']);
				})->
			where('delete_time', null)->
			where('status', 1)->
			order('sort', 'asc')->
			//fetchSql(true)->
			select()
		;

		//var_dump( $rules);

		
		return ret_value(0, "success", ['token' => $this->createToken($data['cid'], $usertoken), 'info' => $userinfo, 'rules' => $rules]);
        //return json([ 'token' => $this->createToken($data['cid'], $user), "code"=> 0, "msg" => "success" ]);
    }

    //生成Token，jwtID为客户端的 验证码ID（也可以服务端提供）
    public function verifyAuth(Request $request)
    {
		//echo( $request->param("token"));
		//echo( $request["get"]("token"));
		//echo( $request->post("token"));
		//echo( "request->get");

    	return $this->verifyToken($request->header("Authorization"));
    	//return this->verifyToken($request->header("token"));
		//$token = $jwt->deserialize($request->param("token"));
    }



    //生成Token，jwtID为客户端的 验证码ID（也可以服务端提供）
    protected function createToken($jwtID, $dataClaim)
    {
		$lms_config = config('lms_common');


        $token = new Token();
        $jwt = new Jwt();

        
        //给Payload设置数据（Claim）
		// Standard claims are supported
		//$token->addClaim(new Claim\Audience('Leamus'));
		//$token->addClaim(new Claim\Expiration(new \DateTime('30 minutes')));
		//$token->addClaim(new Claim\Expiration(new \DateTime('+10 seconds')));
		$token->addClaim(new Claim\Expiration(new \DateTime('+1 days')));
		$token->addClaim(new Claim\IssuedAt(new \DateTime('now')));
		$token->addClaim(new Claim\Issuer($lms_config['JWT_Issuser']));
		$token->addClaim(new Claim\JwtId(uniqid((string)$jwtID, true)));	//session_create_id()
		$token->addClaim(new Claim\NotBefore(new \DateTime('now')));
		$token->addClaim(new Claim\Subject($lms_config['JWT_Subject']));

		// Custom claims are supported
		$token->addClaim(new Claim\PublicClaim('refresh_token_expire', (new \DateTime('+3 days'))->getTimestamp()));
		$token->addClaim(new Claim\PrivateClaim('data', $dataClaim));
		//$token->addClaim(new Claim\PrivateClaim('claim_name2', 'claim_value2'));


		//加密
		$algorithm = new Algorithm\Hs256($lms_config['JWT_Password']);
		$encryption = Factory::create($algorithm);
		$serializedToken = $jwt->serialize($token, $encryption);
		//$token->setSignature($serializedToken);


/*
		echo "<br>------$serializedToken------<br>";
		var_export($serializedToken);
		echo "<br>------token------<br>";
		var_export($token);
		echo "<br>------getSignature------<br>";
		var_export($token->getSignature());
		echo "<br>-----deserialize-------<br>";
		var_dump($jwt->deserialize($serializedToken));
*/

        return $serializedToken;
    }

    //验证Token，过期则重新生成新Token，否则重新登录
    protected function verifyToken($token)
    {
		$lms_config = config('lms_common');
		
        //$token = new Token();
        $jwt = new Jwt();
        
		$algorithm = new Algorithm\Hs256($lms_config['JWT_Password']);
		$encryption = Factory::create($algorithm);
		
		$context = new Context($encryption);
		//$context->setAudience($lms_config['JWT_Audience']);
		$context->setIssuer($lms_config['JWT_Issuser']);
		$context->setSubject($lms_config['JWT_Subject']);

		$token = $jwt->deserialize($token);	//获取token并反序列
		

		$httpCode = 200;
		$retCode = 0;
		$retMsg = "";
		
		try {

			$payload = ($token->getPayload());	//获取Payload
			$exp = ($payload->findClaimByName("exp")->getValue());	//过期时间
			$refresh_token_expire = ($payload->findClaimByName("refresh_token_expire")->getValue());	//刷新时间

			$now = time();
			if($exp < $now) {
				$httpCode = 401;

				//刷新时间也过期，则需要重新登录
				if($refresh_token_expire < $now) {
					$retCode = -1;
				}
				else {	//重新生成Token

					return ret_value($retCode, "refreshToken", $this->createToken(
						//session_create_id(), 
						$payload->findClaimByName("jti")->getValue(), 	//jti也用原来的，最好用新的
						$payload->findClaimByName("data")->getValue()	//数据还是原来的
					), $httpCode);
        			/*return json([ 
        				'data' => $this->createToken(
        					//session_create_id(), 
        					$payload->findClaimByName("jti")->getValue(), 	//jti也用原来的，最好用新的
        					$payload->findClaimByName("data")->getValue()	//数据还是原来的
        				), 
        				"code"=> $retCode, 
        				"msg" => "refreshToken",
        			])->code($httpCode);
					*/
				}
			}

			//系统验证Token
		    $jwt->verify($token, $context);
		}
		catch (\Emarref\Jwt\Exception\InvalidAudienceException $e) {
		    //echo $e->getMessage();
			$httpCode = 401;
		    $retCode = -2;
		    $retMsg = $e->getMessage();
		}
		catch (\Emarref\Jwt\Exception\VerificationException $e) {
			$httpCode = 401;
		    $retCode = -3;
		    $retMsg = $e->getMessage();
		}
		catch (\RuntimeException $e) {
			$httpCode = 401;
		    $retCode = -4;
		    $retMsg = $e->getMessage();
		}
		catch (\Exception $e) {
			$httpCode = 401;
		    $retCode = -5;
		    $retMsg = $e->getMessage();
		}
		
		return ret_value($retCode, $retMsg, null, $httpCode);
        //return json([ 'msg' => $retMsg, "code"=> $retCode ])->code($httpCode);
    }



    //验证码
    public function getCaptcha() {
		return ret_value(0, '', Container::get(\app\services\CaptchaServices::class)->createCaptcha());
	}

	
	public function testCreateCaptcha()
	{
		$d = Container::get(\app\services\CaptchaServices::class)->createCaptcha();
		
        return '<img src="data:image/png;base64,' . $d['img'] . '">';
	}

    public function testVerifyCaptcha(Request $request)
	{
		$res = Container::get(\app\services\CaptchaServices::class)->verifyCaptcha($request->input('code'), $request->input('key'));
		
		if ($res) {
            return ret_value(0, "验证码正确");
		} else {
            return ret_value(-1, "验证码错误");
		}

	}


	/*
    public function testVerifyToken1(Request $request)
    {
	    $token = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MzUzODc5OTgsImlhdCI6MTYzNTM4Nzk4OCwianRpIjoibGVhbXVzLmNuNjE3YTBhNTRlMWU3NDMuODQzNDE2NTIiLCJuYmYiOjE2MzUzODc5ODgsInJlZnJlc2hfdG9rZW5fZXhwaXJlIjoxNjM1NjQ3MTg4LCJ1c2VyIjp7ImxvZ2luX25hbWUiOiJsZWFtdXMiLCJuaWNrX25hbWUiOiJcdTZkZjFcdTY3OTdcdTViNjRcdTllNzAiLCJzZXgiOjEsImlkIjoxfX0.CBAtlkmHs6p4WTUruml-y8EtE59GJzJsP9hACfzM4ig";

		$httpCode = 200;
		$retCode = 0;
		$retMsg = "";

	    
        $jwt = new Jwt();
        
		$algorithm = new Algorithm\Hs256(self::$JWTPassword);
		$encryption = Factory::create($algorithm);

		$context = new Context($encryption);
		//$context->setAudience('audience_3');

		$token = $jwt->deserialize($token);
		//$token = $jwt->deserialize($request->param("token"));
		
	    
		$payload = ($token->getPayload());
		$exp = ($payload->findClaimByName("exp")->getValue());
		$refresh_token_expire = ($payload->findClaimByName("refresh_token_expire")->getValue());

		$now = time();

		
		$user = $payload->findClaimByName("user")->getValue();
		//var_dump( $user);
		$retMsg = $this->createToken($user);
		//var_dump( $retMsg);
		return json([ 'data' => $retMsg, "code"=> $retCode, "msg" => "refreshToken" ])->code($httpCode);
    }
    */
    




}
